﻿using System;
using System.Collections.Generic;
using System.Text;

namespace DbHelper.Data
{
    /// <summary>
    /// 轻量级事务操作(本地事务操作)
    /// <para>注意：只能在同一线程内进行事务</para>
    /// </summary>
    public class MTransScope : IDisposable
    {
        /// <summary>
        /// 数据库事务操作对象
        /// </summary>
        private ITransaction transObj;
        /// <summary>
        /// 数据库操作对象
        /// </summary>
        private IProvider provider;
        /// <summary>
        /// 是否已跳过了 Complete() 函数调用
        /// </summary>
        private bool isJumpedCommit = true;
        /// <summary>
        /// 事务嵌套时顺序，最小值在最外层
        /// </summary>
        private int transIndex = 0;

        /// <summary>
        /// 为注册的第一个数据库服务实例，提供事务服务
        /// </summary>
        public MTransScope()
            : this(MDbService.FirstDriverName)
        {
        }

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="id"></param>
        public MTransScope(string driverName)
        {
            //数据库操作对象
            provider = MDbService.GetProvider(driverName);
            //事务操作对象
            transObj = provider as ITransaction;

            //开始事务之前，关闭数据库
            //原因是防止之前操作忘记关闭数据库，如果事务回滚时会影响到之前的操作
            //if (transObj.TransCount == 0) provider.Close();
            //经过测试：在使用事务之前，不需要关闭连接；补充，可能是内部已经关闭

            //事务记数
            transObj.TransCount++;
            //设置序号
            transIndex = transObj.TransCount;
            //开始事务
            //内部已作判断，多次调用时，只有第一次会生成事务处理对象
            transObj.BeginTransaction();
        }

        /// <summary>
        /// 提交事务
        /// </summary>
        public void Complete()
        {
            if (transIndex == 1)
            {
                transObj.Commit();
            }

            this.isJumpedCommit = false;
        }

        /// <summary>
        /// 释放(一定要调用,如果在事务过程中有异常，这个方法会关闭数据库)
        /// </summary>
        public void Dispose()
        {
            transObj.TransCount = transIndex;
            transObj.TransCount--;

            //如果是最外层嵌套
            if (transIndex == 1)
            {
                //Complete() 函数没有调用
                //则必须回滚
                if (isJumpedCommit)
                {
                    transObj.Rollback();
                }

                //关闭数据库
                IProvider provider = transObj as IProvider;
                provider.Close();
            }
        }
    }
}
